# -*- coding: utf-8 -*-
import hashlib

import requests

from common.mch import db as mch_db
from common.recharge import db as recharge_db
from common.recharge.model import RECHARGE_NOTIFY_STATUS
from common.utils import exceptions as err
from common.utils import track_logging
from common.withdraw import db as withdraw_db
from common.withdraw.model import WITHDRAW_NOTIFY_STATUS

_LOGGER = track_logging.getLogger('fin')


def generate_sign(mch_id, params):
    mch_account = mch_db.get_account(mch_id)
    if not mch_account or not mch_account.enabled:
        raise err.AuthenticateError('mch_id invalid')
    s = ''
    for k in sorted(params.keys()):
        s += '%s=%s&' % (k, params[k])
    s += 'key=%s' % mch_account.api_key
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().upper()
    return sign


def construct_notify_data(order):
    data = {
        'trade_no': order.out_trade_no,
        'mch_id': order.mch_id,
        'pay_result': order.status,
        'total_fee': order.amount,
        'reason': '',
    }
    if order.detail:
        data['reason'] = order.detail
    sign = generate_sign(order.mch_id, data)
    data.update({'sign': sign})
    return data


def notify_mch(order):
    """
    通知商户上分接口
    """
    if order.notify_status == RECHARGE_NOTIFY_STATUS.SUCC:
        _LOGGER.warn('notify_mch, pay[%s] has been notified succ, notify again!', order.id)
    notify_url = order.notify_url
    # construct post data
    data = construct_notify_data(order)
    try:
        _LOGGER.warn('try notify_mch with %s %s' % (notify_url, data))
        res = requests.post(notify_url, data=data, timeout=5)
        if res.text == 'success':
            # update daily remain here
            _LOGGER.info('async notify_mch[%s] succ for order %s', order.mch_id, order.id)
            order = recharge_db.add_notify_success(order.id)
            return True, order
        else:
            _LOGGER.info('async notify_mch[%s] fail for order %s, notify response %s',
                         order.mch_id, order.id, res.text)
    except Exception as e:
        _LOGGER.exception('async notify_mch[%s] exception for order %s',
                          order.mch_id, order.id)
    order = recharge_db.add_notify_fail(order.id)
    return False, order


def construct_withdraw_notify_data(order):
    data = {
        'trade_no': order.out_trade_no,
        'mch_id': order.mch_id,
        'pay_result': order.status,
        'total_fee': order.amount,
        'reason': '',
    }
    if order.detail:
        data['reason'] = order.detail
    channel = withdraw_db.get_channel_by_withdraw_order(order)
    if channel and channel.rate:
        data['handling_fee'] = float('%.2f' % (float(order.amount) * float(channel.rate) / 100.0))
    else:
        data['handling_fee'] = float('%.2f' % (float(order.amount) * 0.8 / 100.0)) # 人工代付 按照0.8计算
    sign = generate_sign(order.mch_id, data)
    data.update({'sign': sign})
    return data


def notify_mch_withdraw(order):
    """
    通知商户下分接口
    """
    if order.notify_status == WITHDRAW_NOTIFY_STATUS.SUCC:
        _LOGGER.warn('notify_mch_withdraw, pay[%s] has been notified succ, notify again!', order.id)
    notify_url = order.notify_url
    # construct post data
    data = construct_withdraw_notify_data(order)
    try:
        _LOGGER.warn('try notify_mch_withdraw with %s, %s' % (notify_url, data))
        res = requests.post(notify_url, data=data, timeout=5)
        if res.text == 'success':
            # update daily remain here
            _LOGGER.info('async notify_mch_withdraw[%s] succ for order %s', order.mch_id, order.id)
            order = withdraw_db.update_withdraw_notify_status(order.id, WITHDRAW_NOTIFY_STATUS.SUCC)
            return True, order
        else:
            _LOGGER.info('async notify_mch_withdraw[%s] fail for order %s, notify response %s',
                         order.mch_id, order.id, res.text)
    except Exception as e:
        _LOGGER.exception('async notify_mch_withdraw[%s] exception for order %s',
                          order.mch_id, order.id)
    order = withdraw_db.update_withdraw_notify_status(order.id, WITHDRAW_NOTIFY_STATUS.FAIL)
    return False, order
